/**
 * Copyright (c) Daniel Centore
 *
 * ex: set filetype=java expandtab tabstop=4 shiftwidth=4 :
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation.
 */
package ronp.anim.gui.strip;

import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;

import ronp.anim.data.filmstrip.Animation;
import ronp.anim.data.filmstrip.Layer;
import ronp.anim.data.objects.RNObject;
import ronp.anim.gui.Gui;
import ronp.anim.gui.SelectedState;
import ronp.anim.gui.SelectedStateListener;

/**
 * The strip we will see along the bottom of the screen containing animation stuff
 * 
 * @author Daniel Centore
 *
 */
public class JAnimationStrip extends JComponent implements MouseListener, MouseMotionListener, SelectedStateListener
{
	private static final long serialVersionUID = 1L;

	public static final int FRAME_WIDTH = 50; // width to make each frame on the bar
	public static final int LAYER_HEIGHT = 50; // height to make each layer line

	private static final int EXTRA_PANES = 20; // extra frames to show to the right

	private Animation anim; // Animation to display
	private SelectedState state; // Current selection
	private Gui gui; // The Gui we are based from

	// dragging stuff
	private int xDiff;
	private int yDiff;
	private boolean isDragging;

	/**
	 * Creates a {@link JAnimationStrip} to display an {@link Animation}
	 * @param anim The {@link Animation} to display
	 */
	public JAnimationStrip(Animation anim, SelectedState state, Gui gui)
	{
		state.registerStateListener(this);
		this.anim = anim;
		this.state = state;
		this.gui = gui;
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
	}

	@Override
	public void paintComponent(Graphics g)
	{
		List<Layer> layers = anim.getLayers();

		int y = 0;
		int maxX = 0;
		boolean gotActive = false;
		for (Layer l : layers)
		{
			Iterator<Entry<Integer, Point>> itr = l.getGrip().entrySet().iterator();

			while (itr.hasNext())
			{
				Entry<Integer, Point> e = itr.next();

				int frame = e.getKey();
				Point pt = e.getValue();

				if (l == state.getActiveLayer() && frame == state.getActiveFrame())
				{
					gotActive = true;
					g.setColor(Color.red);
				}

				int x = frame * FRAME_WIDTH;
				if (x > maxX)
					maxX = x;

				g.drawRect(x, y, FRAME_WIDTH, LAYER_HEIGHT);
				g.drawString("(" + pt.x + ", " + pt.y + ")", x, y + g.getFont().getSize());

				g.setColor(Color.black);
			}

			y += LAYER_HEIGHT;
		}

		this.setPreferredSize(new Dimension(maxX + FRAME_WIDTH * (EXTRA_PANES + 1), y));
		this.revalidate();

		if (!gotActive)
		{
			g.setColor(Color.red);
			g.drawRect(state.getActiveFrame() * FRAME_WIDTH, anim.getLayers().indexOf(state.getActiveLayer()) * LAYER_HEIGHT, FRAME_WIDTH, LAYER_HEIGHT);
		}
	}

	@Override
	public void mouseReleased(MouseEvent e)
	{
		if (isDragging)
		{
			isDragging = false;
			setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			return;
		}

		int x = e.getX();
		int y = e.getY();

		final int layer = y / LAYER_HEIGHT;
		int frame = x / FRAME_WIDTH;

		if (layer >= anim.getLayers().size())
			return;

		state.setActiveLayer(anim.getLayers().get(layer));
		state.setActiveFrame(frame);

		if (e.getButton() == 3)
		{
			JPopupMenu popup = new JPopupMenu();
			JMenuItem menuItem = new JMenuItem("Delete Grip");
			menuItem.addActionListener(new ActionListener()
			{
				@Override
				public void actionPerformed(ActionEvent e)
				{
					Layer l = anim.getLayers().get(layer);
					anim.removeLayer(layer);
					Iterator<RNObject> itr = anim.getObjects().iterator();
					while (itr.hasNext())
						if (itr.next().getGrips().contains(l.getGrip()))
							itr.remove();
				}
			});
			popup.add(menuItem);

			menuItem = new JMenuItem("Delete Frame");
			menuItem.addActionListener(new ActionListener()
			{
				@Override
				public void actionPerformed(ActionEvent e)
				{
					if (state.getActiveFrame() == 0)
						return;

					state.getActiveLayer().getGrip().removePosition(state.getActiveFrame());
					gui.repaint();
				}
			});
			popup.add(menuItem);

			popup.show(this, x, y);
		}

		// this.repaint();
		gui.repaint();
	}

	@Override
	public void mouseClicked(MouseEvent e)
	{
		// unused
	}

	@Override
	public void mouseEntered(MouseEvent e)
	{
		// unused
	}

	@Override
	public void mouseExited(MouseEvent e)
	{
		// unused
	}

	@Override
	public void mousePressed(MouseEvent e)
	{
		xDiff = e.getX();
		yDiff = e.getY();
	}

	@Override
	public void mouseDragged(MouseEvent e)
	{
		setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
		isDragging = true;

		Container c = this.getParent();
		if (c instanceof JViewport)
		{
			JViewport jv = (JViewport) c;
			Point p = jv.getViewPosition();
			int newX = p.x - (e.getX() - xDiff);
			int newY = p.y - (e.getY() - yDiff);

			int maxX = this.getWidth() - jv.getWidth();
			int maxY = this.getHeight() - jv.getHeight();
			if (newX < 0)
				newX = 0;
			if (newX > maxX)
				newX = maxX;
			if (newY < 0)
				newY = 0;
			if (newY > maxY)
				newY = maxY;

			jv.setViewPosition(new Point(newX, newY));
		}
	}

	@Override
	public void mouseMoved(MouseEvent arg0)
	{
		// unused
	}

	@Override
	public void activeFrameSet()
	{
		// ignore
	}

	@Override
	public void activeLayerChanged()
	{
		SwingUtilities.invokeLater(new Runnable()
		{
			@Override
			public void run()
			{
				Container c = JAnimationStrip.this.getParent();
				if (c instanceof JViewport)
				{
					JViewport jv = (JViewport) c;

					Point p = new Point(jv.getViewPosition().x, anim.getLayers().indexOf(state.getActiveLayer()) * LAYER_HEIGHT + 1);
					jv.setViewPosition(p);
				}
			}
		});
	}

}
